fetch로 데이터 주고받기

✒️ 2025-05-28 10:00 내용 수정



API로 데이터 받기

export async function getReviews() {
    const res = await fetch('http://learn.codeit.kr/api/film-reviews');
    const body = await res.json();
    return body;
}
import 'bootstrap/dist/css/bootstrap.min.css';
import '../App.css';
import { getReviews } from '../api';
import { useEffect, useState } from 'react';

function App() {

  const [items, setItems] = useState();

  // api로 데이터를 요청하여 받은 후 저장한다.
  const dataLoad = async () => {
    const {reviews} = await getReviews();
    console.log('data loading complete!')
    setItems(reviews);
  }

  // 렌더링 후 함수를 실행하도록 설정
  useEffect(()=>{
    dataLoad();
  }, []); // clean-up 1회만 실행

  const [flag, setFlag] = useState([true,true,true,true]);
  const order = ['id', 'title', 'rating', 'createdAt'];
  const orderName = ['아이디', '이름', '평점', '개봉일'];

  const ratingDown = (n) => {
    let result = [...items].filter((v)=>{return (v.rating < n)});
    setItems(result);
  }

  const ratingUp = (n) => {
    let result = [...items].filter((v)=>{return (v.rating >= n)});
    setItems(result);
  }

  const dateUp = (n) => {
    let dateCut = new Date(2020, n-1, 2).toISOString().substring(0, 10);
    let result = [...items].filter((v)=>{
      let date = new Date(v.createdAt).toISOString().substring(0, 10);
      return date >= dateCut;
    });
    setItems(result);
  }

  const dateDown = (n) => {
    let dateCut = new Date(2020, n-1, 2).toISOString().substring(0, 10);
    let result = [...items].filter((v)=>{
      let date = new Date(v.createdAt).toISOString().substring(0, 10);
      return date < dateCut;
    });
    setItems(result);
  }
  
  const deleteItem = (id) => {
    let result = [...items].filter((v)=>{return (v.id != id)});
    setItems(result);
  }

  let orderList = (f, i) => {
    let copy = [...flag];
    if (i == 1) {
      if (f) {
        setItems([...items].sort((a, b) => {return a[order[i]].localeCompare(b[order[i]])}));
      } else {
        setItems([...items].sort((a, b) => {return b[order[i]].localeCompare(a[order[i]])}));
      }
    } else {
      if (f) {
        setItems([...items].sort((a, b) => (a[order[i]] - b[order[i]])));
      } else {
        setItems([...items].sort((a, b) => (b[order[i]] - a[order[i]])));
      }
    }

    copy[i] = !copy[i];
    setFlag(copy);
  };

  return (
    <>
      <section className='sec'>
        <div className='container-lg'>
          <h2 className='title'>Movie Reviews</h2>
          <button className='btn btn-success' onClick={dataLoad}>출력하기</button>
          { // 렌더링을 진행하며 api로부터 데이터를 요청해서 저장한 후에만 출력되도록 설정했다.
            // 정렬 버튼이나 필터링 버튼 등이 전부 items에 의존하기에 이 처리를 안 했을 때 오류로 인해 화면이 출력되지 않았다.
            (items) ? 
            <div>
              <div className='btn-wrap'>
                {
                  order.map((el, i)=>{
                    return(
                      <button key={i} className='btn btn-primary' onClick={()=>{orderList(flag[i], i)}} >{
                        (flag[i]) ? orderName[i] + ' 오름차순' : orderName[i] + ' 내림차순'
                      } 정렬</button>
                    )
                  })
                }
              </div>
              <div className='btn-wrap filter'>
                <button className='btn btn-success' onClick={()=>{ratingDown(3)}}>평점 3점 미만만 보기</button>
                <button className='btn btn-success' onClick={()=>{ratingUp(3)}}>평점 3점 이상만 보기</button>
                <button className='btn btn-success' onClick={()=>{dateDown(11)}}>2020년 11월 이전만 보기</button>
                <button className='btn btn-success' onClick={()=>{dateUp(11)}}>2020년 11월 이후만 보기</button>
              </div>
              <ReviewList items={items} deleteItem={deleteItem}></ReviewList>
            </div>
            : null
          }
        </div>
      </section>
    </>
  );
}

// row
function ReviewList(props) {
  let {items, deleteItem} = props; // props 객체 내에서 필요한 데이터 추출

  return(
    <>
      <div className='row row-cols-1'>
        {
          items.map((el)=>{
            return(
              <ReviewListItem key={el.id} item={el} deleteItem={deleteItem}></ReviewListItem>
            )
          })
        }
      </div>
    </>
  )
}

// col
function ReviewListItem(props) { // props 객체 내에서 필요한 데이터 추출
  let {id, title, imgUrl, rating, content, createdAt} = props.item;
  let {deleteItem} = props; 
  let date = new Date(createdAt).toISOString().substring(0, 10);

  return(
    <>
      {
        <div className='col py-3'>
          <div className='gt d-flex'>
            <img src={imgUrl} ult={title}></img>
            <div className='text-box'>
              <ul>
                <li><h3>{title}</h3></li>
                <li>id : {id}</li>
                <li>평점 : {rating}</li>
                <li>생성 날짜 : {date}</li>
                <li>줄거리 : {content}</li>
                <li><button className='btn btn-dark' onClick={()=>{deleteItem(id)}}>삭제</button></li>
              </ul>
            </div>
          </div>
        </div>
      }
    </>
  )
}

export default App;
*{margin:0; padding:0; box-sizing: border-box;}
ul, ol, li{list-style: none;}
a{text-decoration: none;}

body{background-color: #e6e6e6;}

.sec{
  width: 100%;
  padding:50px 0;
}

.sec .btn-wrap .btn{margin:5px;}

.sec .gt{
  padding: 30px;
  background-color: #fff;
  border-radius: 10px;
}

.sec .gt img{width: 20%;}

react fetch 1.png
react fetch 2.png